package com.john.elasticsearch.service.impl;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
import com.alibaba.fastjson.parser.Feature;
import com.john.elasticsearch.common.SearchAfterResult;
import com.john.elasticsearch.common.Utils;
import com.john.elasticsearch.service.ESClientService;
import com.john.utils.token.TokenSing;
import com.john.utils.page.PageInfoUtils;
import org.apache.http.util.EntityUtils;
import org.elasticsearch.action.admin.indices.delete.DeleteIndexRequest;
import org.elasticsearch.action.bulk.BulkProcessor;
import org.elasticsearch.action.delete.DeleteRequest;
import org.elasticsearch.action.get.GetRequest;
import org.elasticsearch.action.get.GetResponse;
import org.elasticsearch.action.index.IndexRequest;
import org.elasticsearch.action.search.SearchRequest;
import org.elasticsearch.action.search.SearchResponse;
import org.elasticsearch.action.update.UpdateRequest;
import org.elasticsearch.client.Request;
import org.elasticsearch.client.RequestOptions;
import org.elasticsearch.client.Response;
import org.elasticsearch.client.RestHighLevelClient;
import org.elasticsearch.client.core.CountRequest;
import org.elasticsearch.client.core.CountResponse;
import org.elasticsearch.client.indices.CreateIndexRequest;
import org.elasticsearch.client.indices.GetIndexRequest;
import org.elasticsearch.common.settings.Settings;
import org.elasticsearch.common.unit.TimeValue;
import org.elasticsearch.common.xcontent.XContentBuilder;
import org.elasticsearch.common.xcontent.XContentFactory;
import org.elasticsearch.common.xcontent.XContentType;
import org.elasticsearch.index.query.BoolQueryBuilder;
import org.elasticsearch.search.SearchHit;
import org.elasticsearch.search.builder.SearchSourceBuilder;
import org.elasticsearch.search.sort.SortOrder;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.TimeUnit;

@Service
public class ESClientServiceImpl<T> implements ESClientService<T> {

    private static final Logger logger = LoggerFactory.getLogger("sys-elasticsearch");

    @Autowired
    private RestHighLevelClient restHighLevelClient;

    @Autowired
    private BulkProcessor bulkProcessor;

    @Override
    public boolean createIndex(String indexName) {
        try {
            indexName = indexName.toLowerCase();
            if(!existIndex(indexName)){
                CreateIndexRequest createIndexRequest = new CreateIndexRequest(indexName);
                //设置分片和副本数
                createIndexRequest.settings(Settings.builder()
                        .put("index.number_of_shards", 5)
                        .put("index.number_of_replicas", 0)
                );
                restHighLevelClient.indices().create(createIndexRequest, RequestOptions.DEFAULT);
            }
        }catch (Exception ex){
            ex.printStackTrace();
            logger.error("创建索引"+indexName+"失败..");
            logger.error(ex.getMessage(),ex);
            return  false;
        }
        logger.info("创建索引"+indexName+"成功..");
        return true;
    }

    @Override
    public boolean existIndex(String indexName) {
        try {
            indexName = indexName.toLowerCase();
            GetIndexRequest request = new GetIndexRequest(indexName);
            boolean result = restHighLevelClient.indices().exists(request, RequestOptions.DEFAULT);
            return result;
        }catch (Exception ex){
            logger.error("查询索引"+indexName+"是否存在失败..");
            logger.error(ex.getMessage(),ex);
            ex.printStackTrace();
            return false;
        }
    }

    @Override
    public boolean removeIndex(String indexName) {
        try {
            indexName = indexName.toLowerCase();
            DeleteIndexRequest deleteIndexRequest = new DeleteIndexRequest(indexName);
            restHighLevelClient.indices().delete(deleteIndexRequest, RequestOptions.DEFAULT);
        }catch (Exception e){
            logger.error(indexName+"索引删除失败");
            logger.error(e.getMessage(),e);
            e.printStackTrace();
            return false;
        }
        logger.info(indexName+"索引删除成功");
        return true;
    }

    @Override
    public void insertDocumentTOES(String indexName, JSONArray source) {
        try {
            indexName = indexName.toLowerCase();
            String id = null;
            for (int i = 0; i < source.size(); i++) {
                  id = source.getJSONObject(i).getString("indexDataId");
                  source.getJSONObject(i).remove("indexDataId");
                  bulkProcessor.add(new IndexRequest(indexName).id(id).source(source.getJSONObject(i).toJSONString(),XContentType.JSON));
            }
        }catch (Exception ex){
            logger.error(indexName+"索引添加数据失败");
            logger.error(ex.getMessage(),ex);
            ex.printStackTrace();
        }
    }

    @Override
    public void insertDocumentTOESOne(String indexName, JSONObject source) {
        try {
            indexName = indexName.toLowerCase();
            String id = source.getString("indexDataId");
            source.remove("indexDataId");
            bulkProcessor.add(new IndexRequest(indexName).id(id).source(source.toJSONString(),XContentType.JSON));
        }catch (Exception ex){
            logger.error(indexName+"索引添加数据失败");
            logger.error(ex.getMessage(),ex);
            ex.printStackTrace();
        }
    }

    @Override
    public String queryAnalyze(String content, String analyzer) {
        try {
            logger.info("进入分词方法");
            logger.info("分词前:"+content);
            analyzer = (analyzer == null || analyzer.equals("")) ? "ik_max_word" : analyzer;
            List<String> list = new ArrayList<String>();
            Request request = new Request("GET", "_analyze");
            JSONObject entity = new JSONObject();
            entity.put("analyzer", analyzer);
            //entity.put("analyzer", "ik_smart");
            entity.put("text", content);
            request.setJsonEntity(entity.toJSONString());
            Response response = restHighLevelClient.getLowLevelClient().performRequest(request);
            JSONObject tokens = JSONObject.parseObject(EntityUtils.toString(response.getEntity()));
            JSONArray arrays = tokens.getJSONArray("tokens");
            for (int i = 0; i < arrays.size(); i++)
            {
                JSONObject obj = JSON.parseObject(arrays.getString(i));
                list.add(obj.getString("token"));
            }
        }catch (Exception ex){
            logger.error("分词失败");
            logger.error(ex.getMessage(),ex);
            ex.printStackTrace();
        }
        return null;
    }

    @Override
    public String queryById(String indexName, String indexId) {
        String result = null;
        try {
            GetRequest getRequest = new GetRequest(indexName, indexId);
            GetResponse getResponse = restHighLevelClient.get(getRequest, RequestOptions.DEFAULT);
            result = getResponse.getSourceAsString();
        }catch (Exception e){
            result = null;
            logger.error("根据索引数据ID查询数据失败");
            logger.error(e.getMessage(),e);
            e.printStackTrace();
        }
        return result;
    }

    @Override
    public void updateById(Object object, String indexName, String indexId) {
        try {
            //修改属性 值
            if (object != null) {
                JSONObject jsonObject = (JSONObject) JSON.toJSON(object);
                XContentBuilder builder = XContentFactory.jsonBuilder();
                builder.startObject();
                for (String key : jsonObject.keySet()){
                    if(jsonObject.get(key) != null && !jsonObject.get(key).equals("")){
                        builder.field(key, jsonObject.get(key));
                    }
                }
                builder.endObject();
                UpdateRequest request = new UpdateRequest(indexName,indexId).doc(builder);
                restHighLevelClient.update(request, RequestOptions.DEFAULT);
            }
        }catch (Exception ex){
            logger.error("根据索引数据ID修改数据失败");
            logger.error(ex.getMessage(),ex);
            ex.printStackTrace();
        }
    }

    @Override
    public void removeById(String indexName, String indexId) {
        try {
            DeleteRequest request = new DeleteRequest(indexName, indexId);
            restHighLevelClient.delete(request, RequestOptions.DEFAULT);
        }catch (Exception ex){
            logger.error("根据索引数据ID删除数据失败");
            logger.error(ex.getMessage(),ex);
            ex.printStackTrace();
        }
    }

    @Override
    public JSONArray queryPage(String indexName, T t, PageInfoUtils pageInfoUtils) {
        JSONArray jsonArray = new JSONArray();
        try {
            CountRequest countRequest = new CountRequest(indexName);
            BoolQueryBuilder boolQueryBuilder = Utils.getBoolQueryBuilder(t);
            countRequest.query(boolQueryBuilder);
            CountResponse countResponse = restHighLevelClient.count(countRequest,RequestOptions.DEFAULT);
            pageInfoUtils.setTotalRecord(Long.valueOf(countResponse.getCount()).intValue());
            if(pageInfoUtils.validator()){
                SearchSourceBuilder searchSourceBuilder = new SearchSourceBuilder();
                // 设置请求超时时间
                searchSourceBuilder.timeout(new TimeValue(20, TimeUnit.SECONDS));
                searchSourceBuilder.query(boolQueryBuilder);
                //起始位置
                searchSourceBuilder.from(pageInfoUtils.getStartRow());
                //每页数量
                searchSourceBuilder.size(pageInfoUtils.getPageSize());
                SearchRequest request = new SearchRequest();
                request.indices(indexName);
                request.source(searchSourceBuilder);
                SearchResponse response =  restHighLevelClient.search(request, RequestOptions.DEFAULT);
                SearchHit[] hits = response.getHits().getHits();
                JSONObject jsonObject = null;
                for (SearchHit hit : hits) {
                    jsonObject = JSON.parseObject(hit.getSourceAsString(),Feature.OrderedField);
                    jsonArray.add(jsonObject);
                }
            }
        }catch (Exception ex){
            ex.printStackTrace();
        }
        return jsonArray;
    }

    @Override
    public JSONArray queryAfterPage(String indexName, T t, SearchAfterResult searchAfterResult) {
        JSONArray jsonArray = new JSONArray();
        try {
            if(searchAfterResult.validator()){

                Object[] objects = null;
                BoolQueryBuilder boolQueryBuilder = Utils.getBoolQueryBuilder(t);
                SearchSourceBuilder searchSourceBuilder = new SearchSourceBuilder();
                searchSourceBuilder.query(boolQueryBuilder);
                searchSourceBuilder.size(searchAfterResult.getPageSize());
                searchSourceBuilder.sort("_id", SortOrder.ASC);
                //如果交替值不为空则按交替值分页查询，反之只查第一页
                if(searchAfterResult.getHands() != null && !searchAfterResult.getHands().equals("")) {
                    objects = new Object[]{TokenSing.decrypt(searchAfterResult.getHands())};
                    searchSourceBuilder.searchAfter(objects);
                }
                SearchRequest searchRequest = new SearchRequest(indexName);
                searchRequest.source(searchSourceBuilder);
                SearchResponse searchResponse = restHighLevelClient.search(searchRequest, RequestOptions.DEFAULT);
                SearchHit[] hits = searchResponse.getHits().getHits();
                if(hits.length > 0){
                    String hands = String.valueOf(hits[hits.length-1].getSortValues()[0]);
                    searchAfterResult.setHands(TokenSing.genAccess_Token(hands,hands));
                    JSONObject jsonObject = null;
                    for (SearchHit searchHit : hits) {
                        jsonObject = JSON.parseObject(searchHit.getSourceAsString(),Feature.OrderedField);
                        jsonArray.add(jsonObject);
                    }
                }else{
                    searchAfterResult.setHands(TokenSing.genAccess_Token(searchAfterResult.getHandsDefault(),searchAfterResult.getHandsDefault()));
                    searchAfterResult.setPageNext(false);
                }
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
        return jsonArray;
    }
}
